using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using TestCases.CompareAssert.Core.Comparers.Interfaces;
using TestCases.CompareAssert.Core.Compositions;
using TestCases.CompareAssert.Core.Interfaces;
using TestCases.CompareAssert.Core.Builders;
using TestCases.Utils.Pairs;

namespace TestCases.CompareAssert.Core.Factories
{
    public class SimpleBuilderFactory : IResultBuilderFactory
    {
        private static readonly SimpleBuilderFactory _instance = new SimpleBuilderFactory();

// Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
// ReSharper disable EmptyConstructor
        static SimpleBuilderFactory()
// ReSharper restore EmptyConstructor
        {
        }

        private SimpleBuilderFactory()
        {
        }

        public static SimpleBuilderFactory Instance
        {
            get { return _instance; }
        }


        public IResultBuilder ForSimpleResult(IObjectComparer comparer, CompareStatus status, IPair<IComparerObject, IComparerObject> pair)
        {
            return new ResultBuilder(comparer, status, pair);
        }

        public IResultBuilder ForDecoratorResult(IObjectComparer comparer, CompareStatus status, IPair<IComparerObject, IComparerObject> pair, IResultProvider child)
        {
            Debug.Assert(status == child.GetStatus(), "For decorator result statuses must be equal");
            return new ReadOnlyBuilder(child.GetResult());
        }

        public IResultBuilder ForCompositeResult<T>(IObjectComparer comparer, CompareStatus status, IPair<IComparerObject, IComparerObject> pair, IList<T> children) where T: IResultProvider
        {
            if (children.Count == 0)
            {
                return new ResultBuilder(comparer, status, pair);
            }
            if (children.Count == 1)
            {
                return ForDecoratorResult(comparer, status, pair, children[0]);
            }

            var causeResults =
                from result in children
                where result.GetStatus() == status
                select result;

            var count = causeResults.Count();
            if (count == 0)
            {
                return new ResultBuilder(comparer, status, pair/*, childResults*/);
            }

            if (count == 1)
            {
                return new ReadOnlyBuilder(causeResults.Single().GetResult());
            }
            
            switch (status)
            {
                case CompareStatus.Equals:
                    return new ResultBuilder(comparer, status, pair/*, causeResults*/);
                    
                case CompareStatus.NotEquals:
                    return new ReadOnlyBuilder(causeResults.Last().GetResult());
                    
                default:
                    return new ResultBuilder(comparer, status, pair, causeResults.GetResults());
            }
        }


    }
}